from datetime import datetime as DateTime
from datetime import date as Date
from dateutil.relativedelta import relativedelta
from datetime import timedelta
import hashlib, calendar
from django.db.models import QuerySet
from django.db import models
from django.utils import timezone
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from backend.eform.models import Eform
from django.utils.text import slugify 

User = get_user_model()

def add_months(sourcedate, months):
    return timezone.localtime(sourcedate) \
        + relativedelta(months=months)
    

# Asset Data Master
# Asset hireachy
class Asset(models.Model):
    asset = models.CharField(max_length=255)
    description = models.CharField(max_length=255)
    level = models.IntegerField()
    deleted = models.BooleanField(default=False)
    def __str__(self):
         return self.asset


# e.g. capacitor
class Component(models.Model):
    component = models.CharField(max_length=255)
    description = models.CharField(max_length=200)
    deleted = models.BooleanField(default=False)


class Equipment(models.Model):
    equipment_class = models.CharField(max_length=50)
    description = models.CharField(max_length=255)
    deleted = models.BooleanField(default=False)

    def __str__(self):
         return self.equipment_class


class EquipmentLevelRelation(models.Model):
    equipment = models.ForeignKey(
        "cews.Equipment",
        on_delete=models.DO_NOTHING
    )

    """     asset = models.ForeignKey(
        "cews.Asset",
        on_delete=models.DO_NOTHING
    ) """

    class Meta:
        ordering = ['-id'] 
        unique_together = (('lv4', 'lv5'),)

    lv1 = models.CharField(max_length=255)
    
    lv2 = models.CharField(max_length=255)

    lv3 = models.CharField(max_length=255)

    lv4 = models.CharField(max_length=255)

    lv5 = models.CharField(max_length=255, default="")

    @property
    def equipment_class(self):
        if self.lv5 != '':
            e = Equipment.objects.get(pk=self.equipment.id).equipment_class
            lv5e = e + '-' + self.lv5
            return lv5e
        e = Equipment.objects.get(pk=self.equipment.id).equipment_class
        return  e


    deleted = models.BooleanField(default=False)


# Parts master
# reference to StockMaster
class Parts(models.Model):
    ## DPRECIATED
    # class CheckInStatusType(models.TextChoices):
    #     NOT_CHECKED_IN = 'N'
    #     CHECKED_IN = 'Y'
    #     CHECKED_OUT = 'O'

    class Meta:
        ordering = ['-id'] 

    parent_part = models.ForeignKey(
        "cews.Parts", on_delete=models.CASCADE, null=True, blank=True
    )

    stock_code = models.CharField(max_length=255)
    serial_no = models.CharField(max_length=255)
    system = models.CharField(max_length=255, blank=True)
    car = models.CharField(max_length=255, default='', blank=True)
    location = models.CharField(max_length=255, default='', blank=True)
    workgroup = models.CharField(max_length=255, default='', blank=True)
    description = models.TextField(default='', blank=True)
    date_code = models.CharField(max_length=255, default='', blank=True)
    sw_ver = models.CharField(max_length=255, default='', blank=True)
    hw_ver = models.CharField(max_length=255, default='', blank=True)
    asset_id = models.CharField(max_length=255, default='', blank=True)
    lv1 = models.CharField(max_length=255, default='', blank=True)
    lv2 = models.CharField(max_length=255, default='', blank=True)
    lv3 = models.CharField(max_length=255, default='', blank=True)
    lv4 = models.CharField(max_length=255, default='', blank=True)
    lv5 = models.CharField(max_length=255, default='', blank=True)
    lv6 = models.CharField(max_length=255, default='', blank=True)
    sequence = models.IntegerField(default=0)
    qr_code = models.CharField(max_length=255, blank=True)
    create = models.DateTimeField(default=timezone.now)
    equipment_class = models.CharField(default="", max_length=255)
    obsolescence = models.IntegerField(default=0) # 此字段已经转移到obs
    stock_item_regular_cycle_month = models.SmallIntegerField(default=36)

    # for check in status
    check_in_status = models.CharField(
        # choices=CheckInStatusType.choices,
        max_length=255,
        default=''
        # default=CheckInStatusType.NOT_CHECKED_IN
    )
    last_check_in = models.DateTimeField(null=True, default=None)
    last_check_out = models.DateTimeField(null=True, default=None)
    last_status = models.CharField(max_length=255, default='New Register')
    last_status_details = models.CharField(
        max_length=100, default='', blank=True
    )

    last_order = models.ForeignKey("cews.RepairOrder",blank=True,null=True,on_delete=models.DO_NOTHING)
    last_order_no = models.CharField(max_length=255, default='', blank=True)
    purchase_date = models.DateField(null=True, default=None)
    image = models.CharField(max_length=100, default='', blank=True)
    io_counter = models.CharField(max_length=100, default='',null=True, blank=True)
    detail_description = models.CharField(max_length=255, blank=True)
    deliver_to = models.CharField(max_length=255, default='', blank=True)
    deleted = models.BooleanField(default=False)

    @property
    def is_outer(self):
        return self.lv4 != '' and self.lv5 == ''

    @property
    def is_parent(self):
        return self.lv5 == "" and self.parent_part is None
    
    @property
    def parent_stock_code(self):
        if self.parent_part:
            return self.parent_part.stock_code
        else:
            return ''
        
    @property
    def parent_serial_no(self):
        if self.parent_part:
            return self.parent_part.serial_no
        else:
            return ''
    
    @property
    def parent_short_description(self):
        if self.parent_part:
            return self.parent_part.description
        else:
            return ''
        
    @property
    def children(self):
        return Parts.objects.filter(parent_part=self)
    
    @property
    def card_installed(self):
        if self.is_parent:
            childs = Parts.objects.filter(parent_part=self)
            return len(childs)
        return '-'
    


class PartsFitmentLog(models.Model):
    stock_item = models.IntegerField()
    part_id = models.IntegerField()
    from_car = models.CharField(max_length=255)
    from_loc = models.CharField(max_length=255)
    from_rack_id = models.IntegerField()
    to_car = models.CharField(max_length=255)
    to_loc = models.CharField(max_length=255)
    to_rack_id = models.IntegerField()
    deleted = models.BooleanField(default=False)


# records components of parts model require replace in several periods
class PartsReplacement(models.Model):
    parts = models.ForeignKey(
        "cews.Parts",
        on_delete=models.DO_NOTHING,
        blank=True,
        null=True
    )

    component = models.CharField(max_length=255, default='')
    
    last_replaced = models.DateTimeField(null=True, blank=True)
    
    period = models.IntegerField(default=0)    
    
    deleted = models.BooleanField(default=False)
    
    @property
    def next_replace(self):
        if self.last_replaced is None:
            return None

        # return self.last_replaced + timedelta(days=self.period * 30) < timezone.now()
        return add_months(self.last_replaced, self.period)
    
    @property
    def is_expired(self):
        if self.last_replaced is None:
            return True

        return self.next_replace < timezone.localtime(timezone.now())
    
    @property
    def diff(self):
        if self.last_replaced is None:
            return 0
        delta = relativedelta(self.next_replace , timezone.localtime(timezone.now()))

        # get months difference
        res_months = delta.months + (delta.years * 12)
        return res_months + 1 

    @property
    def is_critical(self):
        """
            if next replacement is within 90 days, return True
        """
        if self.last_replaced is None:
            return True

        # return self.next_replace < (timezone.localtime(timezone.now()) - timedelta(days=90))
        return self.diff <= 3
# List of SW & HW version of Part
class PartVersion(models.Model):
    class Meta:
        ordering = ['-id']
    parts = models.ForeignKey('cews.Parts', on_delete=models.CASCADE)
    stock_code = models.CharField(max_length=255)
    sw_ver = models.CharField(max_length=255)
    hw_ver = models.CharField(max_length=255)
    update = models.DateTimeField(default=timezone.now)
    deleted = models.BooleanField(default=False)


# Repair Order Header
class RepairOrder(models.Model):
    
    class STATUS(models.TextChoices):
        # IN_PROGRESS = 'IN_PROGRESS'  
        CHECKED_IN = 'CHECKED IN'
        CHECKED_OUT = 'CHECKED OUT'
        COMPLETE = 'COMPLETE'  
        FORCE_COMPLETE = 'COMPLETE*'
        IN_PROCESS = 'IN_PROCESS'
        WAITING_FOR_ENDORSEMENT = 'WAITING_FOR_ENDORSEMENT'
        WAITING_FOR_SIC = 'WAITING_FOR_SIC'
        WAITING_FOR_JOB_COMPLETED = 'WAITING_FOR_JOB_COMPLETED'
    
    class MTCE_TYPE(models.TextChoices):
        CM = 'CM'
        PM = 'PM'
        NM = 'NM'
        IGI = 'IGI'

    class Meta:
        # add permission
        permissions = [
            ["can_force_job_complete", "Can force job complete"]
        ]
        ordering = ['-id']

    # cews_order_no = models.CharField(max_length=255)
    report_date = models.CharField(max_length=255, default='', null=True, blank=True)
    mtce_type = models.CharField(max_length=255, default='', null=True, blank=True)
    # cews work_order_no filled by user
    work_order_no = models.CharField(max_length=255, default='', null=True, blank=True)
    parent_work_order_no = models.CharField(max_length=255, default='', null=True, blank=True)
    department = models.CharField(max_length=255, default='', null=True, blank=True)
    car = models.CharField(max_length=255, default='', null=True, blank=True)
    location = models.CharField(max_length=255, default='', null=True, blank=True)
    car_description = models.CharField(max_length=100, default='', null=True, blank=True)
    remark = models.TextField(null=True, blank=True)
    check_in = models.DateTimeField(default=timezone.now)
    check_out = models.DateTimeField(default=None, null=True, blank=True)
    workgroup = models.CharField(max_length=255, default='', null=True, blank=True)
    system = models.CharField(max_length=255, default='', null=True, blank=True)
    io_counter = models.CharField(max_length=100, default='', null=True, blank=True)
    deliver_to = models.CharField(max_length=255, default='', null=True, blank=True)
    checked = models.SmallIntegerField(default=0)
    status = models.CharField(max_length=255, default=STATUS.CHECKED_IN)
    failure_symptom = models.CharField(blank=True,default='',max_length=200)
    complete_datetime = models.DateTimeField(
        null=True, blank=True, default=None
    )

    complete_by = models.ForeignKey(
        User, on_delete=models.DO_NOTHING, null=True, blank=True
    )

    last_action_date = models.DateField(default=timezone.now)

    created_by = models.ForeignKey(
        User, on_delete=models.DO_NOTHING, null=True, blank=True, default=None, related_name='created_user'
    )

    ## add new fields
    line = models.CharField(max_length=50, default='', null=True, blank=True)
    line_work_order = models.CharField(max_length=255, default='', null=True, blank=True)
    equipment = models.CharField(max_length=255, default='', null=True, blank=True)
    eqpt_description = models.CharField(max_length=255, default='', null=True, blank=True)
    delay_i = models.CharField(max_length=10, default='', null=True, blank=True)
    delay_a = models.CharField(max_length=10, default='', null=True, blank=True)

    ## IGI form
    po_no = models.CharField(max_length=255, default='', null=True, blank=True)
    inspection_no = models.CharField(max_length=255, default='', null=True, blank=True)
    received_date = models.CharField(max_length=255, default='', null=True, blank=True)

    deleted = models.BooleanField(default=False)

    @property
    def order_no(self):
        prefix = 'JO'
        now = DateTime.now()
        YY = now.strftime("%y")
        MM = now.strftime("%m")
        return f"{prefix}{YY}{MM}{self.id:04d}"

    @property
    def primary_part(self): 
        """
            Get the parent repair order part of a repair order

            Args:
                repair_order: RepairOrder instance or RepairOrder ID

            Returns:
                repair_order_part: RepairOrderPart instance
        """

        rop = RepairOrderPart.objects.filter(repair_order=self)

        if rop.__len__ == 1:  # if only one part, return it
            return rop.first()

        rop = rop.filter(parent_part__isnull=True).first()

        if not rop:
            raise self.DoesNotExist()

        return rop
    
    @property
    def repair_details(self):
        return RepairOrderRepairDetails.objects.get(repair_order=self)

    def get_repair_order_part(self):
        return RepairOrderPart.objects.select_related('repair_order').filter(repair_order=self)

    def get_parts_masters(self):
        repair_order_parts = RepairOrderPart.objects.filter(repair_order=self)
        serials = []
        for repair_order_part in repair_order_parts:
            if repair_order_part.serial_no and repair_order_part.serial_no != '':
                serials.append(repair_order_part.serial_no)
        
        parts = Parts.objects.filter(serial_no__in=serials)
        
        return parts

# Repair order line
class RepairOrderPart(models.Model):

    class Meta:
        ordering = ['-id']
    
    class OIType(models.TextChoices):
        OUTER = 'Outer'  # level 4 item
        INNER = 'Inner'  # level 5 item

    class FitmentType(models.TextChoices):
        DEFITMENT = 'Defitment'
        FITMENT = 'Fitment'

    repair_order = models.ForeignKey(
        "cews.RepairOrder", on_delete=models.CASCADE
    )

    # parent_stock_code = models.CharField(max_length=255)
    parent_part = models.ForeignKey(
        "cews.RepairOrderPart", on_delete=models.CASCADE, null=True, blank=True
    )
    
    parts = models.ForeignKey(
        "cews.Parts", on_delete=models.DO_NOTHING, null=True, blank=True
    )

    stock_code = models.CharField(max_length=255)

    outer_inner = models.CharField(
        choices=OIType.choices, default=OIType.OUTER, max_length=255
    )

    # parts serial number
    serial_no = models.CharField(max_length=255)
    system = models.CharField(max_length=255)
    description = models.CharField(max_length=200)
    date_code = models.CharField(max_length=255)
    sw_ver = models.CharField(max_length=255)
    hw_ver = models.CharField(max_length=255)
    fitment = models.CharField(choices=FitmentType.choices, max_length=255)
    last_check_in = models.DateTimeField(null=True, default=None)
    last_check_out = models.DateTimeField(null=True, default=None)
    sequence = models.IntegerField(default=0)
    qr_code = models.CharField(max_length=255)
    status = models.CharField(max_length=255, default="")
    status_details = models.CharField(max_length=100)
    io_counter = models.CharField(max_length=100)
    detail_description = models.CharField(max_length=255)
    deliver_to = models.CharField(max_length=255)
    complete_datetime = models.DateTimeField(
        null=True, blank=True, default=None
    )

    complete_by = models.ForeignKey(
        User, on_delete=models.DO_NOTHING, null=True, blank=True
    )

    ## IGI form
    po_no = models.CharField(max_length=255, default='', null=True, blank=True)
    inspection_no = models.CharField(max_length=255, default='', null=True, blank=True)
    received_date = models.CharField(max_length=255, default='')

    deleted = models.BooleanField(default=False)

    @property
    def is_complete(self):
        return self.complete_datetime is not None

    @property
    def order_no(self):
        return self.repair_order.order_no

    @property
    def part(self):
        # not use anymore, please use self.parts
        return self.parts
        # return Parts.objects.filter(serial_no=self.serial_no,stock_code=self.stock_code).first()

    @property
    def is_parent(self):
        return self.parent_part is None

    @property
    def children(self):
        return RepairOrderPart.objects.filter(parent_part=self)

    ## helper function
    def update_part(self,commit=True):
        """Update part info from repair order part
            Args:
            
                `commit`: save to db or not
        """
        rop = self
        _part = self.part
        
        if _part:
            _part.last_order_no = self.order_no
            _part.last_check_in= rop.last_check_in
            _part.last_check_out= rop.last_check_out
            _part.last_status_details= rop.status_details
            _part.last_status = rop.status
            _part.last_order = self.repair_order
            _part.workgroup = self.repair_order.workgroup
            _part.location = self.repair_order.deliver_to

            if commit:
                _part.save()
        else:
            print(f"part not found {rop.stock_code} {rop.serial_no}")        
            
        return _part

# Action List of Repair
# Repair action master
class RepairAction(models.Model):
    repair_action = models.CharField(max_length=100)
    sequence = models.IntegerField()
    deleted = models.BooleanField(default=False)


# Record all action flow of Repair Order
class RepairOrderAction(models.Model):
    class ResultType(models.TextChoices):
        PASS = "pass"
        FAIL = "fail"
        NONE = ""

    repair_order_id = models.IntegerField()
    repairing_action = models.CharField(max_length=255, default='')
    remarks = models.CharField(max_length=255, default='')
    deliver_to = models.CharField(max_length=255, default='')
    result = models.CharField(max_length=255, default='')
    tester = models.CharField(max_length=255, default='')
    tester_result = models.CharField(
        max_length=255, choices=ResultType.choices
    )

    tester_timestamp = models.DateTimeField(null=True, default=None)
    checker = models.CharField(max_length=255, default='')
    checker_result = models.CharField(
        max_length=255, choices=ResultType.choices
    )

    checker_timestamp = models.DateTimeField(null=True, default=None)
    endorser = models.CharField(max_length=255, default='')
    endorser_result = models.CharField(
        max_length=255, choices=ResultType.choices
    )

    endorser_timestamp = models.DateTimeField(null=True, default=None)
    deleted = models.BooleanField(default=False)


# Record all action flow of Repair Order
class RepairOrderActionLogEquipment(models.Model):
    repair_order_id = models.IntegerField()
    equipment_no = models.CharField(max_length=255)
    e_form_id = models.CharField(max_length=255)
    e_form_name = models.CharField(max_length=255)
    e_form_data_id = models.CharField(max_length=255)
    deleted = models.BooleanField(default=False)
    log = models.ForeignKey(
        "cews.Log", on_delete=models.CASCADE, null=True, blank=True
    )

class RepairOrderEDoc(models.Model):
    repair_order = models.ForeignKey(
        "cews.RepairOrder", on_delete=models.CASCADE
    )
    parts = models.ForeignKey("cews.Parts", on_delete=models.DO_NOTHING,null=True,blank=True)
    
    stock_code = models.CharField(max_length=255, default='')
    serial_no = models.CharField(max_length=255, default='')
    
    file_url = models.CharField(max_length=500, default='')
    doc_name = models.CharField(max_length=100)
    description = models.CharField(max_length=255)
    complete = models.SmallIntegerField(default=0)
    version = models.CharField( max_length=50,default='')
    deleted = models.BooleanField(default=False)


# Order's Part 2
class RepairOrderInspection(models.Model):
    class LiType(models.TextChoices):
        NO = 'N'
        YES = 'Y'

    repair_order_id = models.IntegerField(default=0)
    contractor_liability = models.CharField(
        choices=LiType.choices, default=LiType.NO, max_length=255
    )

    dlc_no = models.CharField(max_length=255)
    urgent_no = models.CharField(max_length=255, default='')
    date = models.CharField(max_length=255)
    item_description = models.CharField(max_length=100, default='')
    mtr_stock_code = models.CharField(max_length=255, default='')
    designer = models.CharField(max_length=255)
    task = models.CharField(max_length=255, default='')
    deleted = models.BooleanField(default=False)


class RepairOrderPartReplacement(models.Model):

    class Meta:
        ordering = ['-id']
    
    repair_order = models.ForeignKey(
        "cews.RepairOrder",
        on_delete=models.DO_NOTHING,
        null=True,
        blank=True
    )

    repair_order_part = models.ForeignKey(
        "cews.RepairOrderPart",
        on_delete=models.DO_NOTHING,
        null=True,
        blank=True
    )

    component = models.CharField(max_length=255)
    
    complete = models.BooleanField(default=False) ##FIXME ??
    period = models.SmallIntegerField(default=0)
    last_replaced = models.DateTimeField(
        null=True,
        blank=True
    )

    replaced = models.DateTimeField( ## complete_datetime
        null=True,
        blank=True
    )

    deleted = models.BooleanField(default=False)
    order_only = models.BooleanField(default=False)
    
    @property
    def next_replace(self):
        if self.replaced is not None:
            return add_months(self.replaced, self.period)

        if self.last_replaced is not None:
            return add_months(self.last_replaced, self.period)
        
        return None
        # return self.last_replaced + timedelta(days=self.period * 30) < timezone.now()
    
    @property
    def is_expired(self):
        if self.last_replaced is None:
            return True

        return self.next_replace < timezone.localtime(timezone.now())
    
    @property
    def diff(self):
        if self.last_replaced is None:
            return 0
        delta = relativedelta(self.next_replace , timezone.localtime(timezone.now()))

        # get months difference
        res_months = delta.months + (delta.years * 12)
        return res_months + 1
    
    @property
    def is_critical(self):
        """
            if next replacement is within 90 days, return True
        """
        if self.last_replaced is None:
            return True

        # return self.next_replace < (timezone.localtime(timezone.now()) - timedelta(days=90))
        return self.diff <= 3

    @property
    def one_time(self):
        return self.period == 0

    @property
    def defitted(self):
        return not self.repair_order_part.serial_no


# Repair order''s part repair log history
class RepairOrderPartStatusLog(models.Model):
    repair_order_id = models.IntegerField()
    stock_code = models.CharField(max_length=255)
    status = models.CharField(max_length=255)
    status_remark = models.CharField(max_length=100)
    date = models.CharField(max_length=255)
    time = models.CharField(max_length=255)
    timestamp = models.DateTimeField(default=timezone.now)
    deleted = models.BooleanField(default=False)


# List of SW & HW version of Part
class RepairOrderPartVersion(models.Model):
    repair_order = models.ForeignKey(
        "cews.RepairOrder",
        on_delete=models.DO_NOTHING,
        null=True,
        blank=True
    )

    repair_order_part = models.ForeignKey(
        "cews.RepairOrderPart",
        on_delete=models.DO_NOTHING,
        null=True,
        blank=True
    )

    stock_code = models.CharField(max_length=255)
    sw_ver = models.CharField(max_length=255)
    hw_ver = models.CharField(max_length=255)
    update = models.DateTimeField(default=timezone.now)
    deleted = models.BooleanField(default=False)

    @property
    def serial_no(self):
        return self.repair_order_part.serial_no


class RepairOrderRepairDetails(models.Model):
    # add permission
    class Meta:
        permissions = [
                ["can_change_repair_detail_after_job_complete", "Can change repair detail after job complete"]
            ]
    repair_order = models.ForeignKey("cews.RepairOrder", on_delete=models.DO_NOTHING)
    repair_details = models.CharField(blank=True,default='',max_length=200)
    failure_symptom = models.CharField(blank=True,default='',max_length=200)
    fault_equipment = models.TextField(blank=True,default='') ## store Json String {SN:True|False}
    actions = models.CharField(blank=True,default='',max_length=200)
    remarks = models.CharField(blank=True,default='',max_length=200)
    delivery_to = models.CharField(blank=True,default='',max_length=200)
    scrapped = models.BooleanField(default=False)
    
    maintenance_type = models.CharField(blank=True,default='',max_length=200)

    class TestResultType(models.TextChoices):
        FF = 'FF'
        SPEC = 'Within spec.'
        NAF = 'NAF'
    
    test_result = models.CharField(
        choices=TestResultType.choices, default='', max_length=50,
        blank=True, null=True
    )
    maintenance_details = models.CharField(blank=True,default='',max_length=200)
    class ResultType(models.TextChoices):
        YES = 'YES'
        NO = 'NO'
    spare_part = models.CharField(
        choices=ResultType.choices, default=None, max_length=20,
        blank=True, null=True
    )
    repeat_within_six_months = models.CharField(
        choices=ResultType.choices, default=None, max_length=20,
        blank=True, null=True
    )
    
    deleted = models.BooleanField(default=False)


# Record all action flow of Repair Order
class RepairOrderTask(models.Model):
    class RoleType(models.TextChoices):
        TESTER = 'tester'
        CHECKER = 'checker'
        ENDORSER = 'endorser'

    class ResultType(models.TextChoices):
        PASS = 'Pass'
        FAIL = 'Fail'
        NA = 'N/A'

    class FlowType(models.TextChoices):
        DONE = 'done'
        VERIFY = 'verify'
        ENDORSE = 'endorse'

    repair_order = models.ForeignKey(
        'cews.RepairOrder', on_delete=models.CASCADE
    )

    repair_order_part = models.ForeignKey(
        'cews.RepairOrderPart', on_delete=models.CASCADE
    )

    stock_item_task = models.ForeignKey(
        'cews.StockItemTask', on_delete=models.CASCADE,
          blank=True,
            null=True
    )

    stock_code = models.CharField(max_length=255,verbose_name ='Stock Code')
    serial_no = models.CharField(max_length=255)
    repairing_action = models.CharField(
        max_length=255, default='', blank=True
    )  # leave empty

    role = models.CharField(
        choices=RoleType.choices, default=RoleType.TESTER, max_length=255
    )

    role_result = models.CharField(
        choices=ResultType.choices, default=ResultType.NA, max_length=255,
        blank=True, null=True
    )

    role_timestamp = models.DateTimeField(null=True, default=timezone.now)

    # eform related
    eform_id = models.IntegerField(blank=True, null=True)
    eform_schema_id = models.IntegerField(blank=True, null=True)
    eform_progress = models.IntegerField(blank=True, null=True, default=-1)
    
    done_by =   models.ForeignKey(User,related_name='done_by',  on_delete=models.DO_NOTHING,blank=True,null=True)
    verify_by =   models.ForeignKey(User,related_name='verify_by',  on_delete=models.DO_NOTHING,blank=True,null=True)
    endorse_by =   models.ForeignKey(User,related_name='endorse_by',  on_delete=models.DO_NOTHING,blank=True,null=True)

    verify_user = models.ForeignKey(User, related_name="donw_user", on_delete=models.DO_NOTHING,blank=True,null=True)
    endorse_user =   models.ForeignKey(User,related_name='endorse_user',  on_delete=models.DO_NOTHING,blank=True,null=True)
    create_user =   models.ForeignKey(User,related_name='create_user',  on_delete=models.DO_NOTHING,blank=True,null=True)
    
    done_by_datetime = models.DateTimeField(blank=True,null=True, default=None)
    verify_by_datetime = models.DateTimeField(blank=True,null=True, default=None)
    endorse_by_datetime = models.DateTimeField(blank=True,null=True, default=None)
    
    is_endorse_required = models.BooleanField(default=True)

    mtce_type = models.CharField(max_length=255, default='')
    cur_tab = models.BooleanField(default=False)
    final_tab = models.BooleanField(default=False)
    # name for save task which created in order
    name = models.CharField(max_length=255, default='', blank=False)
    
    deleted = models.BooleanField(default=False)

    @property
    def has_eform(self):
        return self.eform_schema_id is not None

    @property
    def is_eform_created(self):
        return self.eform_id is not None

    @property
    def task_name(self):
        if self.stock_item_task:
            return f"{self.serial_no}:{self.stock_item_task.name}"
        return f"{self.name}"
# Status Data Master
class Status(models.Model):
    status = models.CharField(max_length=255)
    color = models.CharField(max_length=255)
    sequence = models.IntegerField()
    deleted = models.BooleanField(default=False)

# Stock Master
class StockItem(models.Model):
    class Meta:
        unique_together = (('id', 'stock_code'),)
        ordering = ['-id']

    stock_code = models.CharField(max_length=255)
    system = models.CharField(max_length=255)
    car = models.CharField(max_length=255, default='')
    workgroup = models.CharField(max_length=255, default='')
    description = models.TextField()
    date_code = models.CharField(max_length=255, default='')
    sw_ver = models.CharField(max_length=255, default='')
    hw_ver = models.CharField(max_length=255, default='')
    asset_id = models.CharField(default="", max_length=255)
    equipment_class = models.CharField(default="", max_length=255)
    lv1 = models.CharField(max_length=255, default='')
    lv2 = models.CharField(max_length=255, default='')
    lv3 = models.CharField(max_length=255, default='')
    lv4 = models.CharField(max_length=255, default='')
    lv5 = models.CharField(max_length=255, default='')
    lv6 = models.CharField(max_length=255, default='')
    create = models.DateTimeField(default=timezone.now)
    purchase_date = models.DateField(null=True, default=None)
    image = models.CharField(max_length=100, default='')
    io_counter = models.CharField(max_length=100, default='')
    detail_description = models.CharField(max_length=255, default='')
    deleted = models.BooleanField(default=False)
    obsolescence = models.IntegerField(default=0) 
    global_regular_cycle_month = models.SmallIntegerField(default=36)
    # stock_item_regular_cycle_month = models.SmallIntegerField(default=36)

    @property
    def children(self)->QuerySet['StockItemRelation']:
        return StockItemRelation.objects.select_related(
            'child'
        ).filter(parent=self).order_by('sequence')

    @property
    def is_parent(self):
        '''
           return is parent level item
        '''
        return self.lv5 == '' and self.lv4 != ''

    @property
    def parts_info(self):
        return {
            'stock_code': self.stock_code,
            'system': self.system,
            'description': self.description,
            'date_code': self.date_code,
            'sw_ver': self.sw_ver,
            'hw_ver': self.hw_ver,
            'lv1': self.lv1,
            'lv2': self.lv2,
            'lv3': self.lv3,
            'lv4': self.lv4,
            'lv5': self.lv5,
        }
    
    @property
    def assigned(self):
        if self.is_parent:
            childs = StockItemRelation.objects.filter(parent=self)
            return len(childs)
        return '-'

class StockItemRelation(models.Model):
    parent = models.ForeignKey(
        "cews.StockItem", on_delete=models.CASCADE, related_name='parent'
    )

    child = models.ForeignKey(
        "cews.StockItem", on_delete=models.CASCADE, related_name='child'
    )

    sequence = models.IntegerField(default=0)
    deleted = models.BooleanField(default=False)


def edocUploadTo(instance, filename):
    return '/'.join(
        [
            'eformFile',
            hashlib.sha256(
                str(DateTime.now()).encode()
            ).hexdigest(),
            filename
        ]
    )


# EDoc Data Master
# register related EDoc to stock item (stock code)
class StockItemEDoc(models.Model):
    stock_item = models.ForeignKey(
        "cews.StockItem", on_delete=models.DO_NOTHING
    )
    stock_code = models.CharField(max_length=255, default='')
    doc_name = models.CharField(max_length=100)
    description = models.CharField(max_length=255, default='')
    complete = models.IntegerField(default=0)
    invalid = models.IntegerField(default=0)
    file = models.FileField(upload_to=edocUploadTo, null=True, blank=True)
    version = models.CharField(default="", max_length=255)
    deleted = models.BooleanField(default=False)
    
# Parst EDoc Data Master
# register related EDoc to stock item (stock code)
class PartsEDoc(models.Model):
    stock_item = models.ForeignKey(
        "cews.StockItem", on_delete=models.DO_NOTHING
    )

    class Meta:
        ordering = ['-id']
        
    parts = models.ForeignKey("cews.Parts", on_delete=models.DO_NOTHING)
    
    stock_code = models.CharField(max_length=255, default='')
    serial_no = models.CharField(max_length=255, default='')
    
    doc_name = models.CharField(max_length=100)
    description = models.CharField(max_length=255, default='')
    complete = models.IntegerField(default=0)
    
    invalid = models.IntegerField(default=0)
    file = models.TextField(default='')
    version = models.CharField(default="", max_length=255)
    deleted = models.BooleanField(default=False)
    created = models.DateTimeField(auto_now_add=True, null=True, blank=True)


# Components list of stock item for regular replacement
class StockItemReplacement(models.Model):
    class Meta:
        unique_together = (('stock_item', 'component'),)

    stock_item = models.ForeignKey(
        "cews.StockItem", on_delete=models.DO_NOTHING, blank=True, null=True
    )

    component = models.CharField(max_length=255, default='')
    period = models.IntegerField(default=0)
    deleted = models.BooleanField(default=False)
    
    @property
    def one_time(self):
        return self.period == 0


# Task of Stock Item
class StockItemTask(models.Model):
    '''pair eform with stockitem and task'''
    # stock_item = models.CharField(max_length=255, default='')
    stock_item = models.ForeignKey(
        "cews.StockItem", on_delete=models.DO_NOTHING, blank=True, null=True
    )

    class Meta:
        ordering = ['-id']

    name = models.CharField(max_length=100, default='')

    mtce_type = models.CharField(max_length=255, default='')
    cur_tab = models.BooleanField(default=False)
    final_tab = models.BooleanField(default=False)

    # eform schema id
    eform_schema_id = models.IntegerField(null=True, blank=True)
    deleted = models.BooleanField(default=False)

    formNo = models.CharField(max_length=255, default='')

    @property
    def has_eform(self):
        return self.eform_schema_id is not None


# System Data Master
class System(models.Model):
    system = models.CharField(max_length=255)
    description = models.CharField(max_length=255)
    deleted = models.BooleanField(default=False)

    def __str__(self):
         return self.system


# Task Data Master
class Task(models.Model):
    class Meta:
        ordering = ['-id']

    task = models.CharField(max_length=100)
    wi_no = models.CharField(max_length=255, default='', blank=True)
    description = models.CharField(max_length=255)
    mtce_type = models.CharField(max_length=255, default='')
    cur_tab = models.BooleanField(default=False)
    final_tab = models.BooleanField(default=False)
    deleted = models.BooleanField(default=False)


class VersionMaster(models.Model):
    stock_code = models.CharField(max_length=255)
    hw_ver = models.CharField(max_length=255, blank=True)
    sw_ver = models.CharField(max_length=255, blank=True)
    update = models.DateTimeField(default=timezone.now)
    deleted = models.BooleanField(default=False)

    @property
    def description(self):
        return StockItem.objects.get(stock_code=self.stock_code,deleted=False).description
    
    @property
    def equipment(self):
        return StockItem.objects.get(stock_code=self.stock_code,deleted=False).equipment_class

    class Meta:
        ordering = ['-id']
        # unique_together = ('stock_code','hw_ver','sw_ver')

    @staticmethod
    def get_version_dict(stock_codes: list = []):
        '''
            Helper function to get version dict by stock code

            Return:
                ```python
                {
                    'stock_code':VersionMaster
                }
                ```
        '''
        if len(stock_codes) != 0:
            vms = VersionMaster.objects.filter(stock_code__in=stock_codes)
        else:
            vms = VersionMaster.objects.all()

        version_dict = {}
        
        for v in vms:
            if v.stock_code not in version_dict:
                version_dict[v.stock_code] = []
                
            version_dict[v.stock_code].append(v)

        return version_dict


# IMTE MASTER
class Imte(models.Model):
    imte_no = models.CharField(
        "IMTE_NO", max_length=200, blank=True, null=True
    )

    equipment = models.CharField(
        "EQUIPMENT", max_length=200, blank=True, null=True
    )
    imte_type = models.CharField( max_length=50,default='')
    due_date = models.DateField("DUE_DATE", blank=True, null=True)
    created_on = models.DateTimeField(auto_now_add=True)
    updated_on = models.DateTimeField(auto_now=True)
    deleted = models.BooleanField(default=False)

    class Meta:
        # Add verbose name
        verbose_name = 'IMTE'
        verbose_name_plural = 'IMTE'
        ordering = ['imte_no']
        unique_together = ('imte_no', 'equipment',)


class RepairOrderTaskImte(models.Model):
    imte = models.ForeignKey(
        Imte, on_delete=models.CASCADE
    )

    repair_order_task = models.ForeignKey(
        RepairOrderTask, on_delete=models.CASCADE
    )

    due_date = models.DateField("DUE_DATE", blank=True, null=True)
    equipment = models.CharField(
        "EQUIPMENT", max_length=200, blank=True, null=True,default=''
    )

    imte_no = models.CharField(
        "IMTE_NO", max_length=200, blank=True, null=True,default=''
    )

    
    imte_type = models.CharField(
        "IMTE_NO", max_length=50, blank=True, null=True,default=''
    )

    

    created_on = models.DateTimeField(auto_now_add=True)
    deleted = models.BooleanField(default=False)


class Log(models.Model):
    class Meta:
        ordering = ['-id']
    class LogType(models.TextChoices):
        STOCKMASTER = 'stock master'
        PARTMASTER = 'part master'
        MAINTENANCE = 'maintenance'
        DEPOT = 'depot'

    class CategoryType(models.TextChoices):
        TASK = 'task'
        EDOC = 'edoc'
        IMPWORK = 'improvement work'
        HARDTIME = 'hard time replacement'
        VERSION = 'version'
        ORDER = 'order'
        PART = 'part'
        STOCK = 'stock'
        LOCATION = 'location'

    class ActionType(models.TextChoices):
        COMPLETE = 'complete'
        FITMENT = 'fitment'
        DEFITMENT = 'defitment'
        FINALCHECK = 'final check'
        CREATE = 'create'
        DELETE = 'delete'
        UPDATE = 'update'
        CHECKIN = 'check in'
        CHECKOUT = 'check out'

    log_type = models.CharField(
          choices=LogType.choices, default="", max_length=255
    )

    category_type = models.CharField(
          choices=CategoryType.choices, default="", max_length=255
    )

    action_type = models.CharField(
          choices=ActionType.choices, default="", max_length=255
    )

    payload = models.TextField(default='', blank=True)

    action = models.CharField(default="", max_length=255)

    description = models.TextField(default="")
    
    repair_order = models.IntegerField(default=0)

    # parent_stock_code = models.CharField(max_length=255)
    parent_stock_code = models.CharField(default="", max_length=255)
    
    parent_part = models.CharField(default="", max_length=255)

    part = models.CharField(default="", max_length=255)

    stock_code = models.CharField(default="", max_length=255)
    
    timestamp = models.DateTimeField(default=timezone.now)
    username = models.CharField(default="", max_length=255)
    deleted = models.BooleanField(default=False)

    @property
    def order_no(self):
        if self.repair_order > 0:
            return RepairOrder.objects.get(pk=self.repair_order).order_no
        return ''


class Workgroup(models.Model):
    workgroup = models.CharField(max_length=255)
    description = models.CharField(max_length=255)
    deleted = models.BooleanField(default=False)

    def __str__(self):
        return self.workgroup

class RepairFitmentLog(models.Model):
    repair_order_part_id = models.IntegerField(default=0)
    child_serial = models.CharField(max_length=20)
    deleted = models.BooleanField(default=False)
    timestamp = models.DateTimeField(default=timezone.now)

# class IGIInspection(models.Model):
#     po_no = models.CharField(max_length=255)
#     inspection_no = models.CharField(max_length=255)
#     received_date = models.DateField(null=True, default=None)


class TaskGroup(models.Model):
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    eformSchema_id = models.IntegerField(default=0)
    title = models.CharField(max_length=500, default='')
    formNo = models.CharField(max_length=255, default='')
 
class StockItemGroup(models.Model):
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    stock_id = models.IntegerField(default=0)
    stock_code = models.CharField(max_length=255, default='')
    description = models.CharField(max_length=255, default='')

class TrainborneMaintenanceRecords(models.Model):
    report_date = models.CharField(max_length=255, default='')
    receive_date = models.CharField(max_length=255, default='')
    equipment = models.CharField(max_length=255, default='', null=True, blank=True)
    stock_code = models.CharField(max_length=255, default='', null=True, blank=True)
    serial_no = models.CharField(max_length=255, default='', null=True, blank=True)
    location = models.CharField(max_length=255, default='', null=True, blank=True)
    delay_i = models.CharField(max_length=10, default='', null=True, blank=True)
    delay_a = models.CharField(max_length=10, default='', null=True, blank=True)
    mtce_type = models.CharField(max_length=10, default='', null=True, blank=True)
    line = models.CharField(max_length=50, default='', null=True, blank=True)
    line_work_order = models.CharField(max_length=255, default='', null=True, blank=True)
    work_order_no = models.CharField(max_length=255, default='')
    symptom = models.TextField(default='')
    finding = models.CharField(max_length=10, default='', null=True, blank=True)
    test_result =  models.TextField(default='')
    action = models.TextField(default='')
    eams_completed_date = models.CharField(max_length=255, default='')
    out_date = models.CharField(max_length=255, default='')
    send_to = models.CharField(max_length=255, default='')

class QRMap(models.Model):

    stock_code = models.CharField(max_length=255, blank=False, null=False)
    serial_no = models.CharField(max_length=255, blank=False, null=False)
    qr_string = models.CharField(max_length=255, blank=False, null=False)
    class Meta:
        unique_together = ['stock_code', 'serial_no','qr_string']

class QRMapLocation(models.Model):

    category = models.CharField(max_length=255, blank=False, null=False)
    train = models.CharField(max_length=255, blank=False, null=False)
    qr_string = models.CharField(max_length=255, blank=False, null=False)
    description = models.CharField(max_length=255, default=None, blank=False, null=True, unique=True)
    class Meta:
        unique_together = ['category','train','qr_string']

class QRMapLocationCategory(models.Model):

    type = models.CharField(max_length=255, blank=False, null=False)
    description = models.CharField(max_length=255, blank=False, null=False)



# Improvement work Data Master
# register related Improvement work to stock item (stock code)
def improvementWorkUploadTo(instance, filename):
    return '/'.join(
        [
            'eformFile',
            hashlib.sha256(
                str(DateTime.now()).encode()
            ).hexdigest(),
            filename
        ]
    )
class StockItemImprovementWork(models.Model):
    stock_item = models.ForeignKey(
        "cews.StockItem", on_delete=models.DO_NOTHING
    )
    stock_code = models.CharField(max_length=255, default='')
    doc_name = models.CharField(max_length=100)
    description = models.CharField(max_length=255, default='')
    complete = models.IntegerField(default=0)
    invalid = models.IntegerField(default=0)
    file = models.FileField(upload_to=improvementWorkUploadTo, null=True, blank=True)
    version = models.CharField(default="", max_length=255)
    deleted = models.BooleanField(default=False)
    
# Parst Improvement work Data Master
# register related Improvement work to stock item (stock code)
class PartsImprovementWork(models.Model):
    stock_item = models.ForeignKey(
        "cews.StockItem", on_delete=models.DO_NOTHING
    )

    class Meta:
        ordering = ['-id']
        
    parts = models.ForeignKey("cews.Parts", on_delete=models.DO_NOTHING)
    
    stock_code = models.CharField(max_length=255, default='')
    serial_no = models.CharField(max_length=255, default='')
    
    doc_name = models.CharField(max_length=100)
    description = models.CharField(max_length=255, default='')
    complete = models.IntegerField(default=0)
    
    invalid = models.IntegerField(default=0)
    file = models.TextField(default='')
    version = models.CharField(default="", max_length=255)
    deleted = models.BooleanField(default=False)
    created = models.DateTimeField(auto_now_add=True, null=True, blank=True)



class RepairOrderImprovementWork(models.Model):
    repair_order = models.ForeignKey(
        "cews.RepairOrder", on_delete=models.CASCADE
    )
    parts = models.ForeignKey("cews.Parts", on_delete=models.DO_NOTHING,null=True,blank=True)
    
    stock_code = models.CharField(max_length=255, default='')
    serial_no = models.CharField(max_length=255, default='')
    
    file_url = models.CharField(max_length=500, default='')
    doc_name = models.CharField(max_length=100)
    description = models.CharField(max_length=255)
    complete = models.SmallIntegerField(default=0)
    version = models.CharField( max_length=50,default='')
    deleted = models.BooleanField(default=False)

class Line(models.Model):
    name = models.CharField(max_length=20, default='TWL')

    def save(self, *args, **kwargs):
        self.name = self.name.upper()
        super().save(*args, **kwargs)

    def __str__(self):
        return self.name
    
class DepotList(models.Model):
    depot = models.CharField("DEPOT", max_length=20, unique=True)
    email = models.EmailField("EMAIL", max_length=254, default="", blank=True)

    def __str__(self):
        return f"{self.depot}"

    def save(self, force_insert=False, force_update=False, *args, **kwargs):
        for field_name in ['depot']:
            val = getattr(self, field_name, False)
            if val:
                setattr(self, field_name, val.upper())
        super(DepotList, self).save(*args, **kwargs)

    class Meta:
        # Add verbose name
        verbose_name = 'Depot'
        verbose_name_plural = 'Depot'
        ordering = ['depot']

class CCEmailModel(models.Model):
    emails = models.TextField()